﻿namespace NetFx.AGIL
{
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Text;
  using System.IO;
  using System.Reflection;
  using System.Reflection.Emit;

  /// <summary>
  /// Agil代码书写器。
  /// </summary>
  public class AgilCodeWriter:IDisposable
  {
    #region Statics
    public static AgilCodeWriter of(Module module)
    {
      return new AgilCodeWriter(module);
    }
    public static AgilCodeWriter of(Module module,byte[] codes)
    {
      return new AgilCodeWriter(module,codes);
    }
    public static AgilCodeWriter of(Module module, Stream codeStream)
    {
      return new AgilCodeWriter(module, codeStream);
    }
    public static AgilCodeWriter of(Module module, BinaryWriter binWriter)
    {
      return new AgilCodeWriter(module, binWriter);
    }
    #endregion
    #region Fields
    /// <summary>
    /// IL的代码流。
    /// </summary>
    protected BinaryWriter binWriter = null;
    /// <summary>
    /// IL代码流所在的模块（用于解析Token）。
    /// </summary>
    protected Module module = null;
    #endregion
    #region Properties
    /// <summary>
    /// 获取代码流。
    /// </summary>
    public virtual BinaryWriter BinaryWriter
    {
      get
      {
        return this.binWriter;
      }
    }
    /// <summary>
    /// 判别当前对象是否已经处置。
    /// </summary>
    public virtual bool IsDisposed
    {
      get
      {
        return this.module == null && this.binWriter == null;
      }
    }
    /// <summary>
    /// 判断当前对象的书写器是否可以跳写。
    /// </summary>
    public virtual bool CanSeek
    {
      get
      {
        return this.BinaryWriter.BaseStream.CanSeek;
      }
    }
    /// <summary>
    /// 获取用于解析Token的代码流所在的模块。
    /// </summary>
    public virtual Module Module
    {
      get
      {
        return this.module;
      }
    }
    /// <summary>
    /// 获取代码长度。
    /// </summary>
    public virtual long Offset
    {
      get
      {
        return this.binWriter.BaseStream.Position;
      }
    }
    #endregion
    #region Constructors
    public AgilCodeWriter(Module module)
      : this(module,new MemoryStream())
    {

    }
    public AgilCodeWriter(Module module,byte[] codes):this(module,new MemoryStream(codes))
    {

    }
    public AgilCodeWriter(Module module,Stream codeStream) : this(module,new BinaryWriter(codeStream))
    {
    }
    public AgilCodeWriter(Module module,BinaryWriter binWriter)
    {
      if (module == null) throw new ArgumentNullException("module can not be null!");
      if (binWriter == null) throw new ArgumentNullException("binWriter can not be null!");
      this.module = module;
      this.binWriter = binWriter;
    }
    ~AgilCodeWriter()
    {
      this.Dispose();
    }
    #endregion
    #region Methods
    /// <summary>
    /// 向书写器发射Agil操作。
    /// </summary>
    /// <param name="agil">Agil操作</param>
    public virtual void Write(AgilOperation agil)
    {
      if (agil != null)
      {
        agil.Emit(this.BinaryWriter, this.Module, this.Offset);
      }
    }

    /// <summary>
    /// 跳到指定位置。
    /// </summary>
    /// <param name="offset">指定位置</param>
    /// <param name="origin">相对原点</param>
    public virtual void Seek(long offset, SeekOrigin origin)
    {
      if (this.CanSeek)
      {
        this.BinaryWriter.BaseStream.Seek(offset, origin);
      }
    }
    /// <summary>
    /// 跳到开始。
    /// </summary>
    public virtual void Reset()
    {
      if (this.CanSeek)
      {
        this.Seek(0L, SeekOrigin.Begin);
      }
    }
    /// <summary>
    /// 关闭代码流。
    /// </summary>
    public virtual void Close()
    {
      this.binWriter.Close();
    }
    /// <summary>
    /// 处置当前对象。
    /// </summary>
    public virtual void Dispose()
    {
      this.Dispose(IsDisposed);
    }

    protected virtual void Dispose(bool disposed)
    {
      if (!disposed)
      {
        try
        {
          this.binWriter.Close();
        }
        finally
        {
          this.binWriter = null;
        }
        this.module = null;
      }
    }
    #endregion
  }

}
